package weeny.util;

import java.util.ArrayList;

public class Gaussian {
	public final static float DEF_MU = 0.0f;
	public final static float MIN_SIGMA = 0.0f;
	public final static float DEF_SIGMA = 0.159155f;
	private float mu;
	private float sigma;
	private float k1;
	private float k2;
	public Gaussian() {
		initDefaults();
	}
	public float getMu() {
		return mu;
	}
	public void setMu(float m) {
		mu = m;
	}
	public float getSigma() {
		return sigma;
	}
	public void setSigma(float s) {
		if (s > MIN_SIGMA) {
			k1 = (float) (1 / s / Math.sqrt(2 * Math.PI));
			k2 = -1 / (2 * s * s);
			sigma = s;
		} else {
			System.err.println("Error in anns.Gaussain.setSigma()");
			System.err.println("\tthe value of sigma is to small: " + s);
			System.err.println("\tit must be greater than " + MIN_SIGMA + "\n");
		}
	}
	public void initDefaults() {
		setMu(DEF_MU);
		setSigma(DEF_SIGMA);
	}
	public float getOutput(float x) {
		return (float) (k1 * Math.exp((x - mu) * (x - mu) * k2));
	}
	public float getOutputPrime(float x) {
		return getOutput(x) * -2 * k2 * (x - mu);
	}
	
	
	public static double[] smoothing(double[] values, float tou){
		MyGaussian gauss = new MyGaussian(0, tou);
		double[] result = new double[values.length];
		for(int idx = 0; idx<values.length; idx++){
			result[idx] = 0;
			for(int i = -3; i<=3; i++){
				int j = idx+i;
				if (j<0) {
					j = 0-j;
				}
				if (j>=values.length) {
					j = values.length-(j-values.length)-1;
				}
				result[idx] += gauss.getValue(i)*values[j];
			}
		}
		return result;
	}
	
	

	public static class MyGaussian {
		private double max;
		private ArrayList<Float> list = new ArrayList<Float>();
		private double deltaMax;
		private double gap;
		private float mu;
		private double sigma;
		public MyGaussian(float mu, float sigma2) {
			sigma = Math.sqrt(sigma2);
			this.mu = mu;
			gap = sigma * 3 / 128;
			deltaMax = get3Sigma();
			Gaussian x = new Gaussian();
			x.setSigma((float) (Math.sqrt(sigma2)));
			x.setMu(0);
			float d;
			for (d = 0; d < deltaMax; d += gap) {
				list.add(x.getOutput(d));
			}
			if (d < deltaMax + gap) {
				list.add(x.getOutput((float) deltaMax));
			}
			max = getValue(this.mu);
		}
		public double getMax(){
			return max;
		}
		public double getSigma() {
			return sigma;
		}
		public double get2Sigma() {
			return 2 * sigma;
		}
		public double get3Sigma() {
			return 3 * sigma;
		}
		public float getValue(double mu, double x) {
			double query = Math.abs(x - mu);
			if (Math.abs(query) > deltaMax) {
				return 0f;
			}
			float val = list.get((int) (Math.abs(query) / gap)).floatValue();
			return val;
		}
		public float getValue(double x) {
			return getValue(this.mu, x);
		}
	}
}
